import { RedisPublisher } from "@core/redis/redis.publisher";
import { RedisSubscriber } from "@core/redis/redis.subscriber";
import { eventChannelName } from "@events/event.channel";
import { ServiceCommandType, ServiceCommand } from "@/commands/service.command";
import { createPublisher, createSubscriber } from "@core/redis/message.wrapper";
import { ServiceStatusBo, ServiceStatus } from "@services/service.status.bo";

abstract class ServiceBase {
  name: string;
  private _publisher: RedisPublisher;
  private _subscriber: RedisSubscriber;
  private _status: ServiceStatus = ServiceStatus.deactived;

  constructor() {
    this.name = this.constructor.name;
    this._publisher = createPublisher();
    this._subscriber = createSubscriber();
    this.initialize();
    this.activating();
  }
  private initialize() {
    this.updateServiceStatus(ServiceStatus.initializing);
    this.init();
    this.updateServiceStatus(ServiceStatus.initialized);
  }

  private activating() {
    this.updateServiceStatus(ServiceStatus.activating);
    const observable = this._subscriber.subscribe<ServiceCommand>(
      eventChannelName.systemEventChannel.service_command
    );

    observable.subscribe({
      next: (serviceCommand: ServiceCommand) => {
        if (serviceCommand.serviceName === this.name) {
          switch (serviceCommand.command) {
            case ServiceCommandType.dispose_service:
              this.dispose();
              break;
          }
        }
      },
    });

    this.updateServiceStatus(ServiceStatus.actived);
  }

  protected abstract init(): void;
  protected abstract concreteDispose(): void;

  dispose() {
    this.updateServiceStatus(ServiceStatus.disposing);
    this.concreteDispose();
    this.updateServiceStatus(ServiceStatus.disposed);

    this._publisher.dispose();
    this._subscriber.dispose();
  }

  getStatus(): ServiceStatus {
    return this._status;
  }

  private updateServiceStatus(newStatus: ServiceStatus) {
    this._status = newStatus;
    this.notifyServiceStatusChange(this._status);
  }

  private notifyServiceStatusChange(status: ServiceStatus) {
    const serviceStatus = new ServiceStatusBo(this.name, status);

    this._publisher.publish(
      eventChannelName.systemEventChannel.service_status_update,
      serviceStatus
    );
  }
}

export { ServiceBase };
